#!/usr/bin/env python3
"""
S3 Object Head Tool - HeadObject API 演示工具

这个工具演示如何使用 HeadObject API 获取 S3 存储桶中对象的元数据信息。

使用示例:
   python s3_object_head.py --bucket my-bucket --key test.txt
   python s3_object_head.py --bucket my-bucket --key test.txt --if-match "etag"
   python s3_object_head.py --bucket my-bucket --key test.txt --if-modified-since "2023-01-01"
   python s3_object_head.py --format json
   python s3_object_head.py --verbose
"""

import argparse
import boto3
import json
import sys
from datetime import datetime, timezone
from botocore.config import Config
from botocore.exceptions import ClientError, NoCredentialsError


class ObjectHeadClient:
    """S3 对象元数据获取客户端"""
    
    def __init__(self, endpoint_url='http://localhost:8000', 
                 access_key='AKIAIOSFODNN7EXAMPLE', 
                 secret_key='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
                 region='us-east-1'):
        """
        初始化对象元数据获取客户端
        
        Args:
            endpoint_url: S3 服务端点
            access_key: AWS Access Key
            secret_key: AWS Secret Key
            region: AWS 区域
        """
        self.endpoint_url = endpoint_url
        self.access_key = access_key
        self.secret_key = secret_key
        self.region = region
        self._client = None
    
    def _get_client(self):
        """获取 S3 客户端"""
        if self._client is None:
            self._client = boto3.client(
                's3',
                endpoint_url=self.endpoint_url,
                aws_access_key_id=self.access_key,
                aws_secret_access_key=self.secret_key,
                region_name=self.region,
        config=Config(s3={'addressing_style': 'path'})
    )
        return self._client
    
    def head_object(self, bucket_name, key, if_match=None, if_none_match=None, 
                           if_modified_since=None, if_unmodified_since=None):
        """简单格式获取对象元数据"""
        try:
            s3_client = self._get_client()
            
            # 准备参数
            head_args = {
                'Bucket': bucket_name,
                'Key': key
            }
            
            if if_match:
                head_args['IfMatch'] = if_match
            if if_none_match:
                head_args['IfNoneMatch'] = if_none_match
            if if_modified_since:
                head_args['IfModifiedSince'] = if_modified_since
            if if_unmodified_since:
                head_args['IfUnmodifiedSince'] = if_unmodified_since
            
            print(f"📋 获取对象元数据 s3://{bucket_name}/{key}")
            
            if if_match:
                print(f"   🔍 If-Match: {if_match}")
            if if_none_match:
                print(f"   🔍 If-None-Match: {if_none_match}")
            if if_modified_since:
                print(f"   🔍 If-Modified-Since: {if_modified_since}")
            if if_unmodified_since:
                print(f"   🔍 If-Unmodified-Since: {if_unmodified_since}")
            
            response = s3_client.head_object(**head_args)
            
            print(f"✅ 对象存在！元数据信息：")
            print(f"   🏷️  ETag: {response.get('ETag')}")
            print(f"   📄 Content-Type: {response.get('ContentType')}")
            print(f"   📊 Content-Length: {response.get('ContentLength')} 字节")
            print(f"   📅 Last-Modified: {response.get('LastModified')}")
            
            if response.get('VersionId'):
                print(f"   📋 VersionId: {response.get('VersionId')}")
            
            if response.get('StorageClass'):
                print(f"   💾 Storage-Class: {response.get('StorageClass')}")
            
            # 显示自定义元数据
            metadata = response.get('Metadata', {})
            if metadata:
                print(f"   🏷️  自定义元数据:")
                for key, value in metadata.items():
                    print(f"      • {key}: {value}")
            else:
                print(f"   📭 无自定义元数据")
            
            # 其他可能的头信息
            if response.get('CacheControl'):
                print(f"   🗂️  Cache-Control: {response.get('CacheControl')}")
            if response.get('ContentDisposition'):
                print(f"   📎 Content-Disposition: {response.get('ContentDisposition')}")
            if response.get('ContentEncoding'):
                print(f"   🔤 Content-Encoding: {response.get('ContentEncoding')}")
            if response.get('ContentLanguage'):
                print(f"   🌐 Content-Language: {response.get('ContentLanguage')}")
            
            return True
            
        except ClientError as e:
            error_code = e.response.get('Error', {}).get('Code')
            error_message = e.response.get('Error', {}).get('Message')
            status_code = e.response.get('ResponseMetadata', {}).get('HTTPStatusCode')
            
            if status_code == 304:
                print("✅ 304 Not Modified - 对象未修改")
                return True
            elif status_code == 412:
                print("❌ 412 Precondition Failed - 前置条件失败")
                return False
            elif error_code == 'NoSuchKey':
                print(f"❌ 对象不存在: s3://{bucket_name}/{key}")
                return False
            elif error_code == 'NoSuchBucket':
                print(f"❌ 桶不存在: {bucket_name}")
                return False
            else:
                print(f"❌ 获取元数据失败 [{error_code}]: {error_message}")
                return False
        except NoCredentialsError:
            print("❌ 错误: 未找到 AWS 凭据")
            return False
        except Exception as e:
            print(f"❌ 未知错误: {e}")
            return False
    
    def head_object_verbose(self, bucket_name, key, if_match=None, if_none_match=None, 
                            if_modified_since=None, if_unmodified_since=None):
        """详细格式获取对象元数据"""
        try:
            s3_client = self._get_client()
            
            print("📋 对象元数据详细信息:")
            print("=" * 60)
            print(f"   🗂️  桶名称: {bucket_name}")
            print(f"   🔑 对象键名: {key}")
            print(f"   🌐 区域: {self.region}")
            print(f"   🔗 服务端点: {self.endpoint_url}")
            print(f"   📅 查询时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            
            if if_match:
                print(f"   🔍 If-Match: {if_match}")
            if if_none_match:
                print(f"   🔍 If-None-Match: {if_none_match}")
            if if_modified_since:
                print(f"   🔍 If-Modified-Since: {if_modified_since}")
            if if_unmodified_since:
                print(f"   🔍 If-Unmodified-Since: {if_unmodified_since}")
            
            print()
            
            # 准备参数
            head_args = {
                'Bucket': bucket_name,
                'Key': key
            }
            
            if if_match:
                head_args['IfMatch'] = if_match
            if if_none_match:
                head_args['IfNoneMatch'] = if_none_match
            if if_modified_since:
                head_args['IfModifiedSince'] = if_modified_since
            if if_unmodified_since:
                head_args['IfUnmodifiedSince'] = if_unmodified_since
            
            print(f"   🔧 正在获取对象元数据...")
            response = s3_client.head_object(**head_args)
            
            print(f"   ✅ 对象存在！元数据信息：")
            print(f"      🏷️  ETag: {response.get('ETag')}")
            print(f"      📄 Content-Type: {response.get('ContentType')}")
            print(f"      📊 Content-Length: {response.get('ContentLength')} 字节")
            print(f"      📅 Last-Modified: {response.get('LastModified')}")
            
            if response.get('VersionId'):
                print(f"      📋 VersionId: {response.get('VersionId')}")
            
            if response.get('StorageClass'):
                print(f"      💾 Storage-Class: {response.get('StorageClass')}")
            
            # 显示自定义元数据
            metadata = response.get('Metadata', {})
            if metadata:
                print(f"      🏷️  自定义元数据:")
                for key, value in metadata.items():
                    print(f"         • {key}: {value}")
            else:
                print(f"      📭 无自定义元数据")
            
            # 其他可能的头信息
            if response.get('CacheControl'):
                print(f"      🗂️  Cache-Control: {response.get('CacheControl')}")
            if response.get('ContentDisposition'):
                print(f"      📎 Content-Disposition: {response.get('ContentDisposition')}")
            if response.get('ContentEncoding'):
                print(f"      🔤 Content-Encoding: {response.get('ContentEncoding')}")
            if response.get('ContentLanguage'):
                print(f"      🌐 Content-Language: {response.get('ContentLanguage')}")
            
            print(f"   📊 查询完成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            
            return True
        
        except ClientError as e:
            error_code = e.response.get('Error', {}).get('Code')
            error_message = e.response.get('Error', {}).get('Message')
            status_code = e.response.get('ResponseMetadata', {}).get('HTTPStatusCode')
            
            print(f"   ❌ 获取元数据失败:")
            print(f"      错误代码: {error_code}")
            print(f"      错误信息: {error_message}")
            print(f"      HTTP 状态码: {status_code}")
            
            if status_code == 304:
                print("   ✅ 304 Not Modified - 对象未修改，无需获取")
                return True
            elif status_code == 412:
                print("   ❌ 412 Precondition Failed - 前置条件失败，对象不符合要求")
                return False
            elif error_code == 'NoSuchKey':
                print(f"   ❌ 对象不存在: s3://{bucket_name}/{key}")
                return False
            elif error_code == 'NoSuchBucket':
                print(f"   ❌ 桶不存在: {bucket_name}")
                return False
            else:
                return False
        except NoCredentialsError:
            print("   ❌ 错误: 未找到 AWS 凭据")
            return False
        except Exception as e:
            print(f"   ❌ 未知错误: {e}")
            return False
    
    def head_object_json(self, bucket_name, key, if_match=None, if_none_match=None, 
                         if_modified_since=None, if_unmodified_since=None):
        """JSON 格式获取对象元数据"""
        try:
            s3_client = self._get_client()
            
            # 准备参数
            head_args = {
                'Bucket': bucket_name,
                'Key': key
            }
            
            if if_match:
                head_args['IfMatch'] = if_match
            if if_none_match:
                head_args['IfNoneMatch'] = if_none_match
            if if_modified_since:
                head_args['IfModifiedSince'] = if_modified_since
            if if_unmodified_since:
                head_args['IfUnmodifiedSince'] = if_unmodified_since
            
            response = s3_client.head_object(**head_args)
            
            result = {
                "success": True,
                "bucket_name": bucket_name,
                "key": key,
                "etag": response.get('ETag'),
                "content_type": response.get('ContentType'),
                "content_length": response.get('ContentLength'),
                "last_modified": response.get('LastModified').isoformat() if response.get('LastModified') else None,
                "version_id": response.get('VersionId'),
                "storage_class": response.get('StorageClass'),
                "metadata": response.get('Metadata', {}),
                "cache_control": response.get('CacheControl'),
                "content_disposition": response.get('ContentDisposition'),
                "content_encoding": response.get('ContentEncoding'),
                "content_language": response.get('ContentLanguage'),
                "query_time": datetime.now().isoformat()
            }
            
            # 添加条件请求信息
            if if_match:
                result["if_match"] = if_match
            if if_none_match:
                result["if_none_match"] = if_none_match
            if if_modified_since:
                result["if_modified_since"] = if_modified_since
            if if_unmodified_since:
                result["if_unmodified_since"] = if_unmodified_since
            
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return True
            
        except ClientError as e:
            error_code = e.response.get('Error', {}).get('Code')
            error_message = e.response.get('Error', {}).get('Message')
            status_code = e.response.get('ResponseMetadata', {}).get('HTTPStatusCode')
            
            if status_code == 304:
                result = {
                    "success": True,
                    "bucket_name": bucket_name,
                    "key": key,
                    "status": "not_modified",
                    "message": "对象未修改",
                    "query_time": datetime.now().isoformat()
                }
            else:
                result = {
                    "success": False,
                    "bucket_name": bucket_name,
                    "key": key,
                    "error": error_code,
                    "error_message": error_message,
                    "http_status_code": status_code,
                    "query_time": datetime.now().isoformat()
                }
            
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False
        except NoCredentialsError:
            result = {
                "success": False,
                "error": "NoCredentials",
                "error_message": "未找到 AWS 凭据",
                "query_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False
        except Exception as e:
            result = {
                "success": False,
                "error": "Unknown",
                "error_message": str(e),
                "query_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False


def parse_args():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(
        description='S3 HeadObject API 演示工具',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  %(prog)s --bucket my-bucket --key test.txt                     # 获取对象元数据
  %(prog)s --bucket my-bucket --key test.txt --if-match "etag"   # 条件请求
  %(prog)s --bucket my-bucket --key test.txt --if-modified-since "2023-01-01"  # 时间条件
  %(prog)s --format json                                         # JSON 格式输出
  %(prog)s --verbose                                             # 详细格式输出
  %(prog)s --access-key KEY --secret-key SECRET                  # 指定凭据
        """
    )
    
    parser.add_argument(
        '--host',
        default='http://localhost:8000',
        help='S3 服务端点 (默认: http://localhost:8000)'
    )
    
    parser.add_argument(
        '--access-key',
        default='AKIAIOSFODNN7EXAMPLE',
        help='AWS Access Key (默认: 测试密钥)'
    )
    
    parser.add_argument(
        '--secret-key',
        default='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
        help='AWS Secret Key (默认: 测试密钥)'
    )
    
    parser.add_argument(
        '--region',
        default='us-east-1',
        help='AWS 区域 (默认: us-east-1)'
    )
    
    parser.add_argument(
        '--format',
        choices=['simple', 'verbose', 'json'],
        default='simple',
        help='输出格式 (默认: simple)'
    )
    
    parser.add_argument(
        '--verbose',
        action='store_true',
        help='详细输出模式 (等同于 --format verbose)'
    )
    
    parser.add_argument(
        '--bucket',
        required=True,
        help='桶名称'
    )
    
    parser.add_argument(
        '--key',
        required=True,
        help='对象键名'
    )
    
    parser.add_argument(
        '--if-match',
        help='If-Match 条件（ETag）'
    )
    
    parser.add_argument(
        '--if-none-match',
        help='If-None-Match 条件（ETag）'
    )
    
    parser.add_argument(
        '--if-modified-since',
        help='If-Modified-Since 条件'
    )
    
    parser.add_argument(
        '--if-unmodified-since',
        help='If-Unmodified-Since 条件'
    )
    
    return parser.parse_args()


def main():
    """主函数"""
    args = parse_args()
    
    # 如果指定了 --verbose，覆盖 format 设置
    if args.verbose:
        args.format = 'verbose'
    
    # 创建客户端
    client = ObjectHeadClient(
        endpoint_url=args.host,
        access_key=args.access_key,
        secret_key=args.secret_key,
        region=args.region
    )
    
    # 根据格式执行相应操作
    success = False
    if args.format == 'simple':
        success = client.head_object_simple(
            args.bucket, args.key, args.if_match, args.if_none_match,
            args.if_modified_since, args.if_unmodified_since
        )
    elif args.format == 'verbose':
        success = client.head_object_verbose(
            args.bucket, args.key, args.if_match, args.if_none_match,
            args.if_modified_since, args.if_unmodified_since
        )
    elif args.format == 'json':
        success = client.head_object_json(
            args.bucket, args.key, args.if_match, args.if_none_match,
            args.if_modified_since, args.if_unmodified_since
        )
    
    # 设置退出码
    sys.exit(0 if success else 1)


if __name__ == '__main__':
    main()